var net = require('net');
var uuid = require('node-uuid');
var idGenerator = uuid.generate ? uuid.generate : uuid.v4;

module.exports = light_rpc;

function light_rpc(wrapper) {
	this.wrapper = wrapper;
	this.description = {};
	this.callback = {};

	for (var p in wrapper) {
		this.description[p] = {};
	}

	this.descrStr = command2Str(descrCmd, this.description);
}

var descrCmd = '__DESCR';
var resultCmd = '__RESULT';
light_rpc.prototype.listen = function(port) {
	var self = this;
	var server = net.createServer(function(c) {

		c.on('data', function(data) {
			console.log('on data', data.toString());
			var commands = getCommands(data);
			if (!commands) {
				return;
			}
			commands.forEach(function(cmd) {
				if (cmd.command === descrCmd) {
					console.log('')
					c.write(self.descrStr);
				} else {
					var args = cmd.data.args;
					// constructor callback function
					var cb = function() {
						var innerArgs = [].slice.call(arguments);
						var rCmd = command2Str(resultCmd, {
							id: cmd.data.id,
							args: innerArgs
						});
						console.log(rCmd);
						c.write(rCmd);
					};
					args.push(cb);
					self.wrapper[cmd.command].apply({}, args);
				}
			});
		});
	});
	self.server = server;
	console.log('server listen at' + port);
	server.listen(port);
}

light_rpc.prototype.connect = function(port, host, cb) {
	var self = this;
	var connection = net.createConnection(port, host);
	connection.setKeepAlive(true);

	// 
	connection.on('connect', function() {
		console.log('connection');
		connection.write(command2Str(descrCmd));
	});

	connection.on('data', function(data) {
		console.log('on data', data.toString());
		var commands = getCommands(data);
		if (!commands) {
			return;
		}
		commands.forEach(commandsCallback);
	});

	function commandsCallback(command) {
		if (command.command === resultCmd) {
			var id = command.data.id;
			self.callback[id].apply(this, command.data.args);
			delete self.callback[id];
		} else if (command.command === descrCmd) {
			var remote = {}; // TODO
			for (var name in command.data) {
				(function(cmdName) {
					remote[cmdName] = function() {
						var args = [].slice.call(arguments);
						var lastElement = args.pop();
						if (typeof(lastElement) !== 'function') {
							args.push(lastElement);
						} else {
							self.callback[id] = lastElement;
						}
						var newCmd = command2Str(cmdName, {
							id: id,
							args: args
						});
						console.log("write data:", newCmd.toString('utf8'));
						connection.write(newCmd);
					}
				})(name)
			}
			cb(remote, connection);
		}
	}
}

function command2Str(name, data) {
	var cmd = {
		command: name,
		data: data
	}
	return JSON.stringify(cmd);
}

function getCommands(str) {
	var commands = [];
	try {
		var parsedCmd = JSON.parse(str.toString());
		commands.push(parsedCmd);
		return commands;
	} catch (err) {
		console.log('cannot to command');
		return null;
	}
}